import pygame
import os
from math import floor, ceil
from random import randint
pygame.init()
pygame.mixer.init()

WIDTH, HEIGHT = 900, 600
MouseX, MouseY = 0, 0
clock = pygame.time.Clock()
FGH = "---------------------"
key_pressed = pygame.key.get_pressed()
FPS = 60
inputting = False
command = ""
running = True

# image loads
player_head_img = os.path.join("./img/player_head.png")
block_img = os.path.join("./img/stone.png")
frame_img = os.path.join("./img/frame.png")

# sound loads
Place = lambda: pygame.mixer.Sound(f"./audio/place/Stone_dig{randint(1, 4)}.ogg")
Break = lambda: pygame.mixer.Sound(f"./audio/break/Stone_dig{randint(1, 4)}.ogg")
Step = lambda: pygame.mixer.Sound(f"./audio/step/Stone_step{randint(1, 4)}.ogg")

print(FGH)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Minecraft*  Rungame beta v1.2.2")
pygame.display.set_icon(pygame.image.load(player_head_img))
screen.fill("#B0C4DE")

Blocks = dict()
BlocksRect = list()
all_sprites = pygame.sprite.Group()
block_sprites = pygame.sprite.Group()


def SoundPlay(Sound):
    Sound.play()


def RectNext(Rect1, Rect2):
    if (
            Rect1.right == Rect2.left
            and Rect1.top < Rect2.bottom
            and Rect1.bottom > Rect2.top
    ):
        return True
    if (
            Rect1.left == Rect2.right
            and Rect1.top < Rect2.bottom
            and Rect1.bottom > Rect2.top
    ):
        return True
    if (
            Rect1.bottom == Rect2.top
            and Rect1.left < Rect2.right
            and Rect1.right > Rect2.left
    ):
        return True
    if (
            Rect1.top == Rect2.bottom
            and Rect1.left < Rect2.right
            and Rect1.right > Rect2.left
    ):
        return True
    return False


class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(player_head_img).convert()
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT / 2)
        self.PosX, self.PosY = (self.rect.x / 50, self.rect.y / 50)
        self.speed = 8
        self.y_speed = 0
        self.tick = 0
        self.ticks = floor(self.speed * 2.5)
        self.LastX = self.LastY = 0
        self.jumping = self.LastJumping = False
        self.suspension = self.LastSuspension = True
        self.resur = [9, 6]
        self.ColliderUp = 0
        self.ColliderDown = 0
        self.ColliderLeft = 0
        self.ColliderRight = 0
        self.RectX, self.RectY = (WIDTH / 2, HEIGHT / 2)
        self.RollX, self.RollY = (self.RectX, self.RectY)
        self.ResurBool = True
        self.ResurHigh = HEIGHT / 50 + 8

    def TouchBlock(self):
        for block in BlocksRect:
            if self.rect.colliderect(block):
                return True
        return False

    def positioning(self):
        self.rect.centerx = self.RectX - self.RollX + WIDTH / 2
        self.rect.centery = self.RectY - self.RollY + HEIGHT / 2

    def horizontal(self, x_speed):
        self.RectX += x_speed
        self.positioning()
        while self.TouchBlock():
            if x_speed > 0:
                self.RectX -= 1
            else:
                self.RectX += 1
            self.positioning()

    def vertical(self, y_speed):
        self.RectY += y_speed
        self.positioning()
        while self.TouchBlock():
            if y_speed > 0:
                self.RectY -= 1
                self.jumping = False
            else:
                self.RectY += 1
            self.positioning()
            self.y_speed = 0

    def noise(self):
        self.tick += 1
        for block in BlocksRect:
            if RectNext(self.rect, block) and self.rect.bottom == block.top:
                if self.RectX != self.LastX:
                    if self.tick == self.ticks:
                        Step().play()
                        print("STEP SOUND PLAYING")
                        break

    def IsSuspension(self):
        for block in BlocksRect:
            if RectNext(self.rect, block) and self.rect.bottom == block.top:
                return False
        return True

    def SetPos(self, pos_x, pos_y):
        self.RectX = pos_x * 50
        self.RectY = pos_y * 50
        self.y_speed = 0

    def Resur(self):
        self.SetPos(self.resur[0], self.resur[1])

    def update(self):
        # data update
        if self.y_speed > 100:
            self.y_speed = 100
        self.suspension = self.IsSuspension()
        self.PosX = self.RectX / 50
        self.PosY = self.RectY / 50
        self.ColliderLeft = floor(self.PosX)
        self.ColliderRight = ceil(self.PosX)
        self.ColliderUp = floor(self.PosY)
        self.ColliderDown = ceil(self.PosY)

        # keys check
        if key_pressed[pygame.K_a]:
            self.horizontal(-self.speed)
            self.noise()
        elif key_pressed[pygame.K_d]:
            self.horizontal(self.speed)
            self.noise()
        else:
            self.tick = self.ticks
        if self.tick >= self.ticks:
            self.tick = 0

        if key_pressed[pygame.K_w]:
            if not self.suspension:
                flag = True
                for block in BlocksRect:
                    if RectNext(block, self.rect):
                        if self.rect.top == block.bottom:
                            flag = False
                            break
                if flag:
                    self.y_speed = -19
                    self.jumping = True

        self.y_speed += 1
        self.vertical(self.y_speed)
        self.RollX = self.RectX
        self.RollY = self.RectY
        self.positioning()

        if self.ResurBool:
            if self.PosY > self.ResurHigh:
                print("YOU DIED")
                self.Resur()

        if not self.suspension and self.LastSuspension:
            Step().play()
            print("STEP SOUND PLAYING")
        elif self.RectY > self.LastY:
            for block in BlocksRect:
                if RectNext(self.rect, block) and self.rect.bottom == block.top:
                        Step().play()
                        print("STEP SOUND PLAYING")

        self.LastX, self.LastY = (self.RectX, self.RectY)
        self.LastJumping = self.jumping
        self.LastSuspension = self.suspension


player: Player = Player()

all_sprites.add(player)


class Block(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(block_img).convert()
        self.XPos, self.YPos = (x, y)
        self.RectX = self.YPos * 50
        self.RectY = self.XPos * 50
        self.rect = self.image.get_rect()
        self.rect.centerx = self.XPos * 50 - player.RollX + WIDTH / 2
        self.rect.centery = self.YPos * 50 - player.RollY + HEIGHT / 2
        Blocks[(self.XPos, self.YPos)] = True
        block_sprites.add(self)
        BlocksRect.append(self.rect)

    def positioning(self):
        self.rect.centerx = self.XPos * 50 - player.RollX + WIDTH / 2
        self.rect.centery = self.YPos * 50 - player.RollY + HEIGHT / 2

    def update(self):
        self.positioning()
        if Blocks[(self.XPos, self.YPos)] == 0:
            del Blocks[(self.XPos, self.YPos)]
            block_sprites.remove(self)
            BlocksRect.remove(self)
            self.kill()
            return


class Frame(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(frame_img).convert()
        self.image.set_colorkey("white")
        self.rect = self.image.get_rect()
        self.MousePosX = MouseX + player.RollX - WIDTH / 2
        self.MousePosY = MouseY + player.RollY - HEIGHT / 2
        self.PosX = floor(self.MousePosX / 50)
        self.PosY = floor(self.MousePosY / 50)

    def update(self):
        if MouseX <= 1 or MouseX >= WIDTH-1 or MouseY <= 1 or MouseY >= HEIGHT-1:
            self.image.set_alpha(0)
            return
        else:
            self.image.set_alpha(255)
        # data update
        self.MousePosX = MouseX + player.RollX - WIDTH / 2 + 25
        self.MousePosY = MouseY + player.RollY - HEIGHT / 2 + 25
        self.PosX = floor(self.MousePosX / 50)
        self.PosY = floor(self.MousePosY / 50)
        self.rect.centerx = self.PosX * 50 - player.RollX + WIDTH / 2
        self.rect.centery = self.PosY * 50 - player.RollY + HEIGHT / 2


def BuildBlock(XPos, YPos):
    global Blocks
    if (XPos, YPos) not in Blocks:
        Block(XPos, YPos)


def RemoveBlock(XPos, YPos):
    global Blocks
    if (XPos, YPos) in Blocks:
        Blocks[(XPos, YPos)] = False


def RunCommand(command):
    global Blocks
    if len(command) == 0:
        return
    if command[0] == "gamerule":
        rule = command[1]
        if rule == "PlayerResur":
            xx = int(command[2])
            yy = int(command[3])
            player.resur[0] = xx
            player.resur[1] = yy
        elif rule == "PlayerSpeed":
            spd = int(command[2])
            player.speed = spd
        elif rule == "ResurCheck":
            Boolean = eval(command[2])
            player.ResurBool = Boolean
            if Boolean:
                high = int(command[3])
                player.ResurHigh = high
    elif command[0] == "setblock":
        block = command[3]
        xx = int(command[1])
        yy = int(command[2])
        if block == "stone":
            BuildBlock(xx, yy)
        elif block == "air":
            RemoveBlock(xx, yy)
    elif command[0] == "fill":
        x1 = int(command[1])
        y1 = int(command[2])
        x2 = int(command[3])
        y2 = int(command[4])
        block = command[5]
        for i in range(x1, x2 + 1):
            for j in range(y1, y2 + 1):
                if block == "stone":
                    BuildBlock(i, j)
                elif block == "air":
                    RemoveBlock(i, j)
    elif command[0] == "kill":
        player.Resur()
    elif command[0] == "tp":
        xx = float(command[1])
        yy = float(command[2])
        player.SetPos(xx, yy)
    elif command[0] == "save":
        MapName = command[1]
        MapFile = open(MapName + '.py', 'w')
        MapFile.write('{')
        for (xx, yy) in Blocks:
            MapFile.write("(%d,%d):True," % (xx, yy))
        MapFile.write('}')
    elif command[0] == "loadmap":
        MapName = command[1]
        MapFile = open(MapName + '.py', 'r')
        Blocks = eval(''.join(MapFile.readlines()))
        for (xx, yy) in Blocks:
            Block(xx, yy)


def InformationOutput():
    print()
    print("Block:", round(player.PosX), round(player.PosY))
    print("X:", player.PosX, " ", "Y:", player.PosY)
    print("Collider:", " ", "left", player.ColliderLeft, "right", player.ColliderRight, "up", player.ColliderUp, "down",
          player.ColliderDown)
    print("Jumping:", player.jumping)
    print("Suspensiton:", player.suspension)
    print(FGH)


RunCommand(["fill", "0", "11", "18", "15", "stone"])
FrameGroup = pygame.sprite.Group()
frame = Frame()
FrameGroup.add(frame)

while running:
    clock.tick(FPS)
    MouseX, MouseY = pygame.mouse.get_pos()
    events = pygame.event.get()
    key_pressed = pygame.key.get_pressed()
    for event in events:
        if event.type == pygame.QUIT:
            running = False
            break
        elif event.type == pygame.MOUSEBUTTONDOWN:
            MousePressed = pygame.mouse.get_pressed()
            if 0 <= MouseX <= WIDTH and 0 <= MouseY <= HEIGHT:
                PosX, PosY = (frame.PosX, frame.PosY)
                if (PosX, PosY) not in Blocks and MousePressed[2]:
                    if not ((PosX == player.ColliderLeft and PosY == player.ColliderUp) or \
                            (PosX == player.ColliderRight and PosY == player.ColliderUp) or \
                            (PosX == player.ColliderLeft and PosY == player.ColliderDown) or \
                            (PosX == player.ColliderRight and PosY == player.ColliderDown)):
                        BuildBlock(PosX, PosY)
                        SoundPlay(Place())
                        print("SET BLOCK SOUND PLAY")
                elif (PosX, PosY) in Blocks and MousePressed[0]:
                    RemoveBlock(PosX, PosY)
                    SoundPlay(Break())
        elif event.type == pygame.KEYDOWN:
            if key_pressed[pygame.K_F3]:
                InformationOutput()
            if key_pressed[pygame.K_SLASH]:
                command = input("/")
                RunCommand(command.split())
            if key_pressed[pygame.K_ESCAPE]:
                running = False
                break

    screen.fill("#B0C4DE")

    all_sprites.update()
    block_sprites.update()
    FrameGroup.update()
    all_sprites.draw(screen)
    block_sprites.draw(screen)
    FrameGroup.draw(screen)

    pygame.display.update()

pygame.quit()
